package catalog

import (
	"appengine"
	"appengine/datastore"
	"appengine/urlfetch"
	"appengine/user"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
)

// ID_FORM_KEY contains the key name under which the form stores
// the unique identifier for the catalog entry, if one already exists.
const ID_FORM_KEY = "catalog-id"

var VALID_USERS = map[string]bool{
	"cvsrangasai@gmail.com": true,
	"saisuman@saisuman.com": true,
	"pxsumanx@gmail.com":    true,
	"rangasai@rangasai.com": true,
}

type CatalogEntry struct {
	Title       string
	Description string
	Date        string
	MapUrl      string
	Place       string
	Latitude    float64
	Longitude   float64
	Entity      []string
	Medium      []string
	Size        string
	Tag         []string
	PicUrl      string
}

func init() {
	http.HandleFunc("/", defaultHandler)
	http.HandleFunc("/data/", albumFeedHandler)
	http.HandleFunc("/catalog/read", readHandler)
	http.HandleFunc("/catalog/update", updateHandler)
}

func defaultHandler(w http.ResponseWriter, r *http.Request) {
	if !handleUnauthenticatedUser(appengine.NewContext(r), w, r) {
		return // Already redirected, do nothing.
	}
	// Can now redirect to landing page.
	http.Redirect(w, r, "/catalog", http.StatusSeeOther)
}

/**
 * Simply a proxy to Picasa, used to get around the origin problem without making the Javascript excessively ugly.
 */
func albumFeedHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	client := urlfetch.Client(c)
	fullUrl := "http://picasaweb.google.com" + r.URL.Path + "?" + r.URL.RawQuery
	resp, err := client.Get(fullUrl)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.Copy(w, resp.Body)
}

func handleUnauthenticatedUser(c appengine.Context, w http.ResponseWriter, r *http.Request) bool {
	u := user.Current(c)
	if u != nil {
		if VALID_USERS[u.Email] == true {
			return true
		}
		http.Error(w, "You do not have access to this application.", http.StatusForbidden)
		return false

	}
	loginUrl, err := user.LoginURL(c, "/catalog")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return false // Could not get a login url.
	}
	http.Redirect(w, r, loginUrl, http.StatusSeeOther)
	return false
}

func formValueStrings(values []string) []string {
	return values
}

func formValueString(values []string) string {
	if len(values) > 0 {
		return values[0]
	}
	return ""

}
func formValueDate(values []string) string {
	return formValueString(values)
}

func formValueFloat(values []string) float64 {
	if len(values) == 0 {
		return 9999.0 // JSON don't like no NaNs.
	}
	f, err := strconv.ParseFloat(values[0], 64)
	if err != nil {
		return 9999.0 // JSON don't like non NaNs.
	}
	return f
}

func readHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	if !handleUnauthenticatedUser(c, w, r) {
		return
	}

	queryParams := r.URL.Query()
	if queryParams[ID_FORM_KEY] == nil {
		http.Error(w, "No catalog ID provided.", http.StatusInternalServerError)
		return
	}
	k := datastore.NewKey(c, "CatalogEntry", queryParams[ID_FORM_KEY][0], 0, nil)
	e := new(CatalogEntry)
	err := datastore.Get(c, k, e)
	if err == datastore.ErrNoSuchEntity {
		fmt.Fprintf(w, "{}")
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	bytes, err := json.Marshal(e)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "%s", string(bytes))
}

/**
 * Stores information on a single catalog entry.
 */
func updateHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	if !handleUnauthenticatedUser(c, w, r) {
		return // Already redirected, do nothing.
	}

	if err := r.ParseForm(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if r.Form[ID_FORM_KEY] == nil || len(r.Form[ID_FORM_KEY]) == 0 {
		http.Error(w, "No catalog ID provided.", http.StatusInternalServerError)
		return
	}
	catalogId := r.Form[ID_FORM_KEY][0]

	entry := new(CatalogEntry)
	entry.Title = formValueString(r.Form["title"])
	entry.Description = formValueString(r.Form["description"])
	entry.Date = formValueDate(r.Form["date"])
	entry.Latitude = formValueFloat(r.Form["latitude"])
	entry.Longitude = formValueFloat(r.Form["longitude"])
	entry.MapUrl = formValueString(r.Form["mapurl"])
	entry.Place = formValueString(r.Form["place"])
	entry.Entity = formValueStrings(r.Form["entity"])
	entry.Medium = formValueStrings(r.Form["medium"])
	entry.Size = formValueString(r.Form["size"])
	entry.Tag = formValueStrings(r.Form["tag"])
	entry.PicUrl = formValueString(r.Form["picurl"])

	// Now let's write it out!
	k := datastore.NewKey(c, "CatalogEntry", catalogId, 0, nil)
	_, err := datastore.Put(c, k, entry)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "{}")
}
